package com.gitee.huanminabc.jlambda_sql.warapper;


import com.gitee.huanminabc.jlambda_sql.SQL;
import com.gitee.huanminabc.jlambda_sql.SqlWrapper;
import com.gitee.huanminabc.jlambda_sql.TableInfo;
import com.gitee.huanminabc.jlambda_sql.base.PreTable;
import com.gitee.huanminabc.jlambda_sql.base.SqlFun;
import com.gitee.huanminabc.jlambda_sql.segments.SqlStructureImplSegments;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;

/**
 * 全套sql自定义sql
 * @param <T>
 */
@EqualsAndHashCode(callSuper = true)
@Slf4j
public class LambdaWrapper<T> extends SqlStructureImplSegments<T, LambdaWrapper<T>>  implements SqlWrapper {

    private LambdaWrapper(Class<T> entityClass, SQL sql) {
        super(entityClass, sql);
    }

    //关联主表, 这个实体用于接收查询结果的,无须和数据库表完全对应
    public static <T> LambdaWrapper<T> create(Class<T> entity){
        return new LambdaWrapper<>(entity, new SQL());
    }
    public static <T> LambdaWrapper<T> create(Class<T> entity, String alias){
        LambdaWrapper<T> tLambdaWrapper = new LambdaWrapper<>(entity, new SQL());
        tLambdaWrapper.getTableInfo().setAlias(alias); //因为是最开始的表,所以别名不可能会有重复,这里就不用检查
        return tLambdaWrapper;
    }

    /**
     * 不使用lambda表达式 ,自己写sql  站位符为?
     * 此方法使用后不能再使用lambda后续的方法了
     * @param entity  主表实体类  ,用于接收查询结果
     * @param sql sql语句
     * @param params 参数
     * @return LambdaSQL
     * @param <T>
     */
    public static <T> LambdaWrapper<T> create(Class<T> entity, String sql, Object... params){
        SQL sql1 = new SQL();
        sql1.setSql(sql);
        sql1.setParams(Arrays.asList(params));
        return new LambdaWrapper<>(entity, sql1);
    }



    //===========================下面是增强方法===========================

    //内部使用
    private static <T> LambdaWrapper<T> _create_(Class<?> entity){
        return  create((Class<T>) entity);
    }



    //预注册表 ,用于后面列你需要自定义sql的时候,就必须提前将需要的表注册进来,操作的时候使用别名,不然生成的表别名啥的都是内置的

    public LambdaWrapper<T> PRE_TABLE(PreTable... preTables){
        for (PreTable preTable : preTables) {
            PRE_TABLE(preTable.getEntityClass(),preTable.getAlias());
        }
        return typedThis;
    }
    private LambdaWrapper<T> PRE_TABLE(Class<?> entityClass, String alias){
        //判断之前是否已经加入过
        TableInfo tableInfo = getEntityClassList().get(entityClass.getName());
        if(tableInfo!=null){
            throw new RuntimeException("表已经存在");
        }
        TableInfo tableInfo1 = this.createTableInfo(entityClass);
        tableInfo1.setAlias(alias);
        return typedThis;
    }


    //通过其他的LambdaSQL生成子查询
    public <Y> LambdaWrapper<T> SON(LambdaWrapper<Y> lambdaWrapper){
        _SON_(lambdaWrapper, null);
        return typedThis;
    }
    //通过其他的LambdaSQL生成子查询,只是说简化了创建lambdaSQL的过程
    public <Y> LambdaWrapper<T> SON(Class<Y> entityClass, SqlFun<LambdaWrapper<Y>, LambdaWrapper<Y>> sqlFunctions) {
        LambdaWrapper<Y> objectLambdaWrapper = LambdaWrapper._create_(entityClass);
        //拿到父的别名索引,让子的别名索引从父的别名索引开始
        objectLambdaWrapper.setAliasIndex(this.getAliasIndex());
        _SON_(objectLambdaWrapper,sqlFunctions);
        return typedThis;
    }
    //通过主表生成的子查询表
    public LambdaWrapper<T> SON(SqlFun<LambdaWrapper<T>, LambdaWrapper<T>> sqlFunctions) {
        Class<?> entityClass = this.getTableInfo().getEntityClass();
        LambdaWrapper<T> objectLambdaWrapper = LambdaWrapper._create_(entityClass);
        _SON_(objectLambdaWrapper,sqlFunctions);
        return typedThis;
    }
    private <Y> void _SON_(LambdaWrapper<Y> objectLambdaWrapper, SqlFun<LambdaWrapper<Y>, LambdaWrapper<Y>> sqlFunctions) {
        //拿到父的别名索引,让子的别名索引从父的别名索引开始
        objectLambdaWrapper.setAliasIndex(this.getAliasIndex());
        String newAlias = objectLambdaWrapper.getNewAlias(); //子的别名使用新的别名作为主表的别名
        objectLambdaWrapper.getTableInfo().setAlias(newAlias);
        if(sqlFunctions!=null){
            LambdaWrapper<Y> apply = sqlFunctions.apply(objectLambdaWrapper);
            SQL().append(LEFT_BRACKET()).append(apply.SQL().toString()).append(RIGHT_BRACKET());
        }else{
            SQL().append(LEFT_BRACKET()).append(objectLambdaWrapper.SQL().toString()).append(RIGHT_BRACKET());
        }
        //子别名索引赋值给父,这样就能保证别名永远是递增的
        this.setAliasIndex(objectLambdaWrapper.getAliasIndex());
    }

    //通过其他的LambdaSQL生成子查询表
    public <Y> LambdaWrapper<T> TABLE(LambdaWrapper<Y> objectLambdaWrapper){
        _TABLE_(objectLambdaWrapper,null);
        return typedThis;
    }
    //通过其他的LambdaSQL生成子查询表, 多个虚拟表
    public LambdaWrapper<T> TABLES(LambdaWrapper... objectLambdaWrapper){
        //判断是否为空
        if(objectLambdaWrapper ==null|| objectLambdaWrapper.length==0){
            throw new RuntimeException("TABLES方法参数不能为空");
        }
        for (LambdaWrapper yLambdaWrapper : objectLambdaWrapper) {
            _TABLE_(yLambdaWrapper,null);
            SQL().append(",");
        }
        //删除最后一个逗号
        SQL().deleteCharAt(SQL().length()-1);
        return typedThis;
    }

    //通过主表生成的子查询表
    public  <Y> LambdaWrapper<T> TABLE(SqlFun<LambdaWrapper<T>, LambdaWrapper<T>> sqlFunctions){
        Class<?> entityClass = this.getTableInfo().getEntityClass();
        LambdaWrapper<T> objectLambdaWrapper = LambdaWrapper._create_(entityClass);
        _TABLE_(objectLambdaWrapper,sqlFunctions);
        return typedThis;
    }

    //通过其他的LambdaSQL生成子查询表 , 只是说简化了创建lambdaSQL的过程
    public  <Y> LambdaWrapper<T> TABLE(Class<Y> entityClass, SqlFun<LambdaWrapper<Y>, LambdaWrapper<Y>> sqlFunctions){
        LambdaWrapper<Y> objectLambdaWrapper = LambdaWrapper._create_(entityClass);
        _TABLE_(objectLambdaWrapper,sqlFunctions);
        return typedThis;
    }

    private   <Y>  void   _TABLE_(LambdaWrapper<Y> objectLambdaWrapper, SqlFun<LambdaWrapper<Y>, LambdaWrapper<Y>> sqlFunctions){
        //拿到父的别名索引,让子的别名索引从父的别名索引开始
        objectLambdaWrapper.setAliasIndex(this.getAliasIndex());
        String newAlias = objectLambdaWrapper.getNewAlias(); //子的别名使用新的别名作为主表的别名
        objectLambdaWrapper.getTableInfo().setAlias(newAlias);
        if(sqlFunctions!=null){
            LambdaWrapper<Y> apply = sqlFunctions.apply(objectLambdaWrapper);
            SQL().append(LEFT_BRACKET()).append("\n").append(apply.SQL().toString()).append(RIGHT_BRACKET());
        }else{
            SQL().append(LEFT_BRACKET()).append("\n").append(objectLambdaWrapper.SQL().toString()).append(RIGHT_BRACKET());
        }
        //子别名索引赋值给父,这样就能保证别名永远是递增的
        this.setAliasIndex(objectLambdaWrapper.getAliasIndex());
        //新的别名 作为虚拟表的别名
        String newAlias1 = this.getNewAlias();
        SQL().append(" ").append(newAlias1);

        //将子查询的主表映射加入到父查询的映射中
        TableInfo tableInfo = objectLambdaWrapper.getTableInfo();
        tableInfo.setAlias(newAlias1);
        //判断类似是否是父主表类
        if (tableInfo.getEntityClass().equals(this.getTableInfo().getEntityClass())) {
            //如果是父主表类,那么修改父主表的别名
            this.getTableInfo().setAlias(newAlias1);
        }else {
            super.addTable(tableInfo);
        }
    }


}
